#!/usr/bin/python
import os, sys, re
from optparse import OptionParser
from string import Template

tests = {}
tests_setup = {}
tests_destroy = {}
tests_logfile = "testlog.html"

def createOptParser():
    usage = "scan.py [options] headerfile... "
    version = "0.1"
    description = '''
This script is used to scan header file of unit test project.
'''
    parser = OptionParser(usage = usage, version = version,
                          description = description)
    parser.add_option("-o", "--outfile", dest = "outfile",
                      help = "output source filename", metavar = "FILE")
    parser.add_option("-d", "--description", dest = "description",
                      help = "description of this test")
    parser.add_option("-l", "--logfile", dest = "logfile",
                      help = "filename of the log.", metavar = "FILE")
    return parser

def printHeader(outfile, headers):
    '''
    print the header of the test source.
    '''
    comment = '''// Unit test source file generated by CTF scanner
// Please don't modify this file unless you know exactly what you
// are doing.
'''
    header = '''
#include "ctf.h"\n
'''
    include_format = '#include "{0}"\n'
    main_header = '''
main(int argc, char *argv[])
{
    ctf_begin("Unit test created by CTF generator", "'''

    outfile.write(comment)
    outfile.write(header)
    for header in headers:
        outfile.write(include_format.format(
                os.path.basename(header)))
        
    outfile.write(main_header + tests_logfile + '");\n')

def printTestCases(outfile):
    '''create test suit and add test cases into corresponding test suit'''
    for module in tests:
        test_cases = tests[module]
        setup_string = tests_setup.has_key(module) and tests_setup[module] or "NULL"
        destroy_string = tests_destroy.has_key(module) and tests_destroy[module] or "NULL"
        testsuit_str = '\n    ctf_testsuit ts_{0} = ctf_ts_init("{0}", {1}, {2});\n'
        outfile.write(testsuit_str.format(module, setup_string, destroy_string))
        for test_case in test_cases:
            testcase_str = '    ctf_tc_add("{1}", ts_{0}, {0}_{1});\n'
            outfile.write(testcase_str.format(module, test_case))
                          
def printTail(outfile):
    '''print other code of test source'''
    tail = """
    ctf_run();
    ctf_end();
    return 0;
}
"""
    outfile.write(tail)

def printSource(filename, headers):
    '''print the test source'''
    outfile = open(filename, mode = 'w')
    printHeader(outfile, headers)
    printTestCases(outfile)
    printTail(outfile)

def addTest(module, test_case):
    if tests.has_key(module):
        tests[module].append(test_case)
    else:
        tests[module] = [test_case]

def scanTest(filename):
    
    test_string = r'''\b\s*TEST\((\s*[^,]+),\s *([^, ]+)\)\s #function header
\{
([^\}]*) #function body
\}
'''
    content = open(filename).read()
    find_test = re.compile(test_string, re.VERBOSE)
    for module, func, body in find_test.findall(content):
        addTest(module, func)

    # how to allow "_" in stup function name?
    setup_func = re.compile(r"SETUP\(\s*([^\(\)]+)\s*\)")
    destroy_func = re.compile(r"DESTROY\(\s*([^\(\)]+)\s*\)")
    for setup in setup_func.findall(content):
        tests_setup[setup] = setup + "_setup"
    for destroy in destroy_func.findall(content):
        tests_destroy[destroy] = destroy + "_destroy"

if __name__ == '__main__':
    parser = createOptParser()
    (options, args) = parser.parse_args()

    if len(args) < 1 or not options or not options.outfile:
        parser.print_help(sys.stderr)
        sys.exit(0)

    for arg in args:
        scanTest(arg)
    
    printSource(options.outfile, args)
